package jeconbond.experiment.generate;

import jeconbond.automata.INode;
import jeconbond.automata.IOutcome;
import jeconbond.automata.AutomataUtils;
import jeconbond.economic.resources.ResourceBasket;
import jeconbond.economic.resources.ResourceStorrage;
import jeconbond.economic.resources.IResourceType;
import jeconbond.experiment.market.MerchantExperimentSettings;
import jeconbond.experiment.process.IJEBExperimentContext;

import java.util.Collection;
import java.util.List;
import java.util.ArrayList;
import java.util.LinkedHashMap;

public class ProfitableNormFormGenerateDecorator implements IAutomataGenerator {
	private IAutomataGenerator subgenerator;

	public ProfitableNormFormGenerateDecorator(IAutomataGenerator subgenerator) {
		this.subgenerator = subgenerator;
	}

	@Override
	public INode[] generate(IJEBExperimentContext experimentContext) {
		INode[] automatas = subgenerator.generate(experimentContext);
		makeProfitable(experimentContext, automatas);
		normaliseOutsRequirements(automatas);
		return automatas;
	}

	private void normaliseOutsRequirements(INode[] automatas) {
		List<IOutcome> allOuts = new ArrayList<IOutcome>();
		for (INode root : automatas) {
			List<IOutcome> outs = AutomataUtils.dfsGetAllOutcomes(root);
			allOuts.addAll(outs);
		}

		ResourceStorrage tmpStorr = new ResourceStorrage(new LinkedHashMap<IResourceType, ResourceBasket>());
		for (IOutcome out : allOuts) {
			out.setRequirements(
				normaliseOne(out.getRequirements(), tmpStorr)
			);
		}
	}

	private ResourceBasket[] normaliseOne(ResourceBasket[] baskets, ResourceStorrage tmpStorr) {
		tmpStorr.clear();
		for (ResourceBasket rb : baskets) {
			tmpStorr.storeResource(rb);
		}
		Collection<ResourceBasket> normalisedColl = tmpStorr.listBaskets();
		int nonZeroCount = getNonZeroCount(normalisedColl);
		if (nonZeroCount == baskets.length) {
			return baskets;
		}
		int p = 0;
		ResourceBasket[] result = new ResourceBasket[nonZeroCount];
		for (ResourceBasket rb : normalisedColl) {
			if (rb.value > 0.0) {
				result[p++] = new ResourceBasket(rb);
			}
		}
		if (p < result.length) throw new RuntimeException("Unexpected.");
		return result;
	}

	private static int getNonZeroCount(Collection<ResourceBasket> normalisedColl) {
		int result = 0;
		for (ResourceBasket rb : normalisedColl) {
			if (rb.value > 0.0) {
				result++;
			}
		}
		return result;
	}

	private void makeProfitable(
			IJEBExperimentContext experimentContext,
			INode[] automatas
	) {
		MerchantExperimentSettings settings = 
			(MerchantExperimentSettings) experimentContext.getExperimentSettings();
		AutomataUtils.makeProfitable(
				automatas,
				settings.resources,
				settings.produceProfit,
				settings.profitCostManagerFactory.newManager(experimentContext)
		);
	}
}
